/* External definitions for inventory system. */

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define a (double) 16807.0
#define m (double) 2147483647.0
#define q (double) 127773.0
#define r (float) 2836.0

int   bigs, next_event_type, num_events,
      num_days, num_values_demand, smalls;
float area_holding, area_shortage, holding_cost, incremental_cost, maxlag,
      mean_interdemand, minlag, prob_distrib_demand[26], setup_cost,
      shortage_cost, sim_time, time_last_event, time_next_event[5],
      total_ordering_cost, mean_demand, inv_level, initial_inv_level, amount, weeks_with_shortage;
double hi, test, lo, *seed1, *seed2, *seed3;
FILE  *infile, *outfile;

void  initialize(void);
void  timing(void);
void  order_arrival(void);
void  demand(void);
void  evaluate(void);
void  report(void);
void  update_time_avg_stats(void);
float expon(float mean, double *ex_seed);
//int   random_integer(float prob_distrib []);
float randomvar(double *rand_seed);
//float uniform(float a, float b, double *seed);


main()  /* Main function. */
{
    int i, num_policies;

    /* Open input and output files. */

    infile  = fopen("inv.in",  "r");
    outfile = fopen("inv.out", "w");
	seed1 = malloc(sizeof(double));
	seed2 = malloc(sizeof(double));
	seed3 = malloc(sizeof(double));
	mean_demand = 5.0;
	holding_cost = 5.2;
	shortage_cost = 520;
	setup_cost = 15;
	incremental_cost = 2;
    /* Specify the number of events for the timing function. */

    num_events = 4;

    /* Read input parameters. */

    fscanf(infile, "%f %d %d",
           &initial_inv_level, &num_days, &num_policies);

    /* Write report heading and input parameters. */

    fprintf(outfile, "Single-product inventory system\n\n");
    fprintf(outfile, "Initial inventory level%24f tons\n\n", initial_inv_level);
    fprintf(outfile, "Demand mean = 5.0 tons");
    fprintf(outfile, "\n\nInterdemand time are U[0,1] days\n\n");
    fprintf(outfile, "Order lead times are U[0,1] days\n\n");
    fprintf(outfile, "Length of the simulation%23d days\n\n", num_days);
    fprintf(outfile, "K =%6.1f   i =%6.1f   h =%6.1f   pi =%6.1f\n\n",
            setup_cost, incremental_cost, holding_cost, shortage_cost);
    fprintf(outfile, "Number of policies%29d\n\n", num_policies);
    fprintf(outfile, "                 Annual");
    fprintf(outfile, "          Annual		Annual	      Total		%%Weeks with\n");
    fprintf(outfile, "  Policy      Ordering Costs  ");
    fprintf(outfile, " Holding cost  shortage cost   ordering cost	shortage");

    /* Run the simulation varying the inventory policy. */

    for (i = 1; i <= num_policies; ++i) {

        /* Read the inventory policy, and initialize the simulation. */

        fscanf(infile, "%d %d", &smalls, &bigs);
        initialize();

        /* Run the simulation until it terminates after an end-simulation event
           (type 3) occurs. */

        do {

            /* Determine the next event. */

            timing();

            /* Update time-average statistical accumulators. */

            update_time_avg_stats();

            /* Invoke the appropriate event function. */

            switch (next_event_type) {
                case 1:
                    order_arrival();
                    break;
                case 2:
                    demand();
                    break;
                case 4:
                    evaluate();
                    break;
                case 3:
                    if(inv_level < 0 && sim_time > 0)
                    {
                    	weeks_with_shortage++;
                    }
                    report();
                    break;
            }

        /* If the event just executed was not the end-simulation event (type 3),
           continue simulating.  Otherwise, end the simulation for the current
           (s,S) pair and go on to the next pair (if any). */

        } while (next_event_type != 3);
    }

    /* End the simulations. */

    fclose(infile);
    fclose(outfile);

    return 0;
}


void initialize(void)  /* Initialization function. */
{
    /* Initialize the simulation clock. */

    sim_time = 0.0;

    /* Initialize the state variables. */
	*seed1 = 1985072130;
	*seed2 = 748932582;
	*seed3 = 1631331038;
    inv_level       = initial_inv_level;
    time_last_event = 0.0;

    /* Initialize the statistical counters. */

    total_ordering_cost = 0.0;
    area_holding        = 0.0;
    area_shortage       = 0.0;
    weeks_with_shortage = 0;

    /* Initialize the event list.  Since no order is outstanding, the order-
       arrival event is eliminated from consideration. */

    time_next_event[1] = 1.0e+30;
    time_next_event[2] = sim_time + randomvar(seed1);
    time_next_event[3] = num_days;
    time_next_event[4] = 0.0;
}


void timing(void)  /* Timing function. */
{
    int   i;
    float min_time_next_event = 1.0e+29;

    next_event_type = 0;

    /* Determine the event type of the next event to occur. */

    for (i = 1; i <= num_events; ++i)
        if (time_next_event[i] < min_time_next_event) {
            min_time_next_event = time_next_event[i];
            next_event_type     = i;
        }

    /* Check to see whether the event list is empty. */

    if (next_event_type == 0) {

        /* The event list is empty, so stop the simulation */

        fprintf(outfile, "\nEvent list empty at time %f", sim_time);
        exit(1);
    }

    /* The event list is not empty, so advance the simulation clock. */
   // printf("Timing check: %f %f\n", sim_time, inv_level);

    sim_time = min_time_next_event;
}


void order_arrival(void)  /* Order arrival event function. */
{
    /* Increment the inventory level by the amount ordered. */

    inv_level += amount;
    /* Since no order is now outstanding, eliminate the order-arrival event from
       consideration. */

    time_next_event[1] = 1.0e+30;
}


void demand(void)  /* Demand event function. */
{
    /* Decrement the inventory level by a generated demand size. */

    inv_level -= expon(mean_demand, seed3);

    /* Schedule the time of the next demand. */

    time_next_event[2] = sim_time + randomvar(seed1);
}


void evaluate(void)  /* Inventory-evaluation event function. */
{
    /* Check whether the inventory level is less than smalls. */

    if (inv_level < smalls) {

        /* The inventory level is less than smalls, so place an order for the
           appropriate amount. */

        amount               = bigs - inv_level;
        total_ordering_cost += setup_cost + incremental_cost * amount;

        /* Schedule the arrival of the order. */
        time_next_event[1] = sim_time + randomvar(seed2);

    }
    if(inv_level < 0 && sim_time > 0)
    {
    	weeks_with_shortage++;
    }
    /* Regardless of the place-order decision, schedule the next inventory
       evaluation. */


    time_next_event[4] = sim_time + 7.0;
}


void report(void)  /* Report generator function. */
{
    /* Compute and write estimates of desired measures of performance. */

    float avg_holding_cost, avg_ordering_cost, avg_shortage_cost;

    avg_ordering_cost = total_ordering_cost / num_days;
    avg_holding_cost  = holding_cost * area_holding / num_days;
    avg_shortage_cost = shortage_cost * area_shortage / num_days;
    fprintf(outfile, "\n\n(%3d,%3d)%15.2f%15.2f%15.2f%15.2f%15.2f",
            smalls, bigs, avg_ordering_cost*364.0,
            avg_holding_cost, avg_shortage_cost, avg_ordering_cost*364.0 + avg_holding_cost + avg_shortage_cost, weeks_with_shortage/(ceil((float)num_days/7)));
}


void update_time_avg_stats(void)  /* Update area accumulators for time-average
                                     statistics. */
{
    float time_since_last_event;

    /* Compute time since last event, and update last-event-time marker. */

    time_since_last_event = sim_time - time_last_event;
    time_last_event       = sim_time;

    /* Determine the status of the inventory level during the previous interval.
       If the inventory level during the previous interval was negative, update
       area_shortage.  If it was positive, update area_holding.  If it was zero,
       no update is needed. */

    if (inv_level < 0)
        area_shortage -= inv_level * time_since_last_event;
    else if (inv_level > 0)
        area_holding  += inv_level * time_since_last_event;
}


float expon(float mean, double* ex_seed)  /* Exponential variate generation function. */
{
    /* Return an exponential random variate with mean "mean". */

    return -mean * log(randomvar(ex_seed));
}


/*int random_integer(float prob_distrib[])  /* Random integer generation
                                             function. *
{
    int   i;
    float u;

    /* Generate a U(0,1) random variate. *

    u = randomvar(seed);

    /* Return a random integer in accordance with the (cumulative) distribution
       function prob_distrib. *

    for (i = 1; u >= prob_distrib[i]; ++i)
        ;
    return i;
}*/


/*float uniform(float a, float b, double *seed)  /* Uniform variate generation function. *
{
    /* Return a U(a,b) random variate. *

    return a + randomvar(seed) * (b - a);
}*/
float randomvar(double *rand_seed)
{
	float my_random;
	hi=floor(*rand_seed/q);
	lo=*rand_seed-q*hi;
	test=a*lo-r*hi;
	if (test>0.0)
		*rand_seed=test;
	else
		*rand_seed=test+m;
	my_random=*rand_seed/m;
	return my_random;
}
